// This is the main DLL file.

#include "stdafx.h"

#include "libvln.h"

namespace irontree {

	namespace libvln {

		u_int64 Cvln::BinaryAddition(u_int32 lhs, u_int32 rhs)
		{
			if (lhs == 0) return rhs;
			u_int64 carry, buffer = lhs;
			while ( rhs )
			{
				carry = buffer & (u_int64)rhs;
				buffer = buffer ^ (u_int64)rhs;
				rhs = carry << 1;
			}
			return lhs;
		}

		void Cvln::shiftLeft(u_int64 distance)
		{
			if (distance == 0) 
				return;
			u_int64 oldCount = itemCount;
			resize(distance+itemCount);
			memmove(&itemPool[distance], itemPool, (size_t)(oldCount*byteLength));
			memset(itemPool, 0, (size_t)(distance*byteLength));
		}

		std::string Cvln::toString()
		{
			char buffer[10];
			memset(&buffer[0], 0, 10*sizeof(char));
			std::string output;
			for(int64 i=(itemCount-1);i>=0;--i)
			{
				sprintf(buffer, "%09d", itemPool[i]);
				output.append( buffer);
			}
			// remove leading zeros
			size_t zeroCount = 0;
			while (output.find('0', zeroCount) == zeroCount)
				++zeroCount;
			return output.substr(zeroCount);
		}

		void Cvln::resize(const u_int64 &length)
		{
			// realloc() handles null itemPool (allocates), zero-length (frees),
			// and ofcourse reallocates more or less memory
			itemPool = (u_int32*)realloc(itemPool, (size_t)(byteLength*length));
			memset(&itemPool[itemCount], 0, (size_t)(byteLength*(length-itemCount)));
			itemCount = length;
		}

		void Cvln::setItem(const u_int64 &index, const u_int32 &value)
		{
			if (index+1 > itemCount) resize(index+1);
			memcpy(&itemPool[index], &value, byteLength);
		}

		Cvln Cvln::firstBig(u_int64 number)
		{
				Cvln result;
				if (number > itemCount)
					number = itemCount;
				for(int64 i = itemCount-1; i > (int64)(itemCount-number-1); --i)
					result.setItem(i, itemPool[i]);
				return result;
		}

		Cvln Cvln::power(const Cvln &base, const Cvln &exponent)
		{
			if (exponent == 0)
				return Cvln(1);
			if (exponent == 1)
				return base;
			Cvln result(base);
			for (Cvln i = 2; i<=exponent; i = i + 1)
			{
				result = result * base;
			}
			return result;
		}

		u_int32 Cvln::operator[](const u_int64 &index)
		{
			if ( index >= itemCount ) 
				return 0;
			return itemPool[index];
		}

		Cvln& Cvln::operator=(const Cvln &rhs)
		{
			if (this == &rhs)
				return *this;
			// copy class variables
			itemCount = rhs.itemCount;
			// reallocate itemPool memory to 
			// new size and copy data
			itemPool = (u_int32*)realloc(itemPool, (size_t)(byteLength*itemCount));
			memcpy(&itemPool[0], &rhs.itemPool[0], (size_t)(byteLength*itemCount));
			return *this;
		}

		bool operator==(const Cvln &left, const Cvln &right)
		{
			/*
			// different length numbers are not equal
			if (left.itemCount != right.itemCount)
				return false;
			// from "back to front" of array
			for(int64 i=(left.itemCount-1);i>=0;--i)
			{
				// test for failure condition on each item
				if (left.itemPool[i] != right.itemPool[i])
					return false;
			}
			// no failure conditions found
			return true;
			*/

			// This might be more work having potentially twice time but 
			// it seems to fit with the logic used in the
			// other relational operators
			// when left is not less than right, and right is not less than left
			// then they must be equal
			return !(left<right) && !(right<left);
		}

		bool operator!=(const Cvln &left, const Cvln &right)
		{
			return !(left == right);
		}

		bool operator<(const Cvln &left, const Cvln &right)
		{
			// if the left side has less items than the right
			// then, it must be true that the left side
			// is less than the right side
			if ( left.itemCount < right.itemCount )
				return true;
			// if the left side has more items
			// than the right, then it must be that
			// the left side is not less than the right
			else if ( left.itemCount > right.itemCount )
				return false;
			// inspect each item in the pool and compare.
			// start at the greater end, larger index (i) values.
			// if the left side item is ever less than the right
			// side then the left side number is indeed
			// less than the right side
			for(int64 i=(left.itemCount-1);i>=0;--i)
			{
				if ( left.itemPool[i] < right.itemPool[i] )
					return true;
			}
			return false;
		}

		bool operator<=(const Cvln &left, const Cvln &right)
		{
			return !(right < left);
		}

		bool operator>(const Cvln &left, const Cvln &right)
		{
			return (right < left);
		}

		bool operator>=(const Cvln &left, const Cvln &right)
		{
			return !(left < right);
		}

		Cvln operator-(const Cvln &left, const Cvln &right)
		{
			// todo:
			// 2's compliment subtraction.

			u_int64 length, buffer;
			Cvln result, lhs(left), rhs(right);
			bool carryBit = false;

			// set the length to the longer item
			// initilize the result to the shorter item
			if (left.itemCount < right.itemCount)
			{
				length = right.itemCount;
				lhs.resize(length);
			} 
			else
			{
				length = left.itemCount;
				rhs.resize(length);
			}
			result.resize(length);

			// add each element from longer item
			// to the result (shorter item)
			for (u_int64 i=0;i<length;++i)
			{
				buffer = 0xFFFFFFFF && 
					Cvln::BinaryAddition(
						lhs.itemPool[i], 
						Cvln::BinaryAddition(
							Cvln::BinaryAddition(
								~((u_int32)(rhs.itemPool[i])), 
								1), 
							carryBit));

				if (buffer >= 0x80000000)
					carryBit = true;
				else
					carryBit = false;

				result.setItem(i, (u_int32)buffer);
			}
			result.setSign(carryBit);

			return result;
		}

		Cvln operator+(const Cvln &left, const Cvln &right)
		{
			u_int64 length, buffer;
			Cvln result;
			const Cvln *pLower; 
			bool carryBit = false;

			// set the length to the longer item
			// initilize the result to the shorter item
			if (left.itemCount < right.itemCount)
			{
				result = left;
				pLower = &right;
			} 
			else
			{
				result = right;
				pLower = &left;
			}
			length = pLower->itemCount;
			result.resize(length);

			// add each element from longer item
			// to the result (shorter item)
			for (u_int64 i=0;i<length;++i)
			{
				if ( result.itemPool[i]==0 && pLower->itemPool[i]==0 )
					continue;
				buffer = Cvln::BinaryAddition(result.itemPool[i], Cvln::BinaryAddition(pLower->itemPool[i], carryBit));
				if ( buffer >= LIMIT )
				{
					buffer = buffer % LIMIT;
					carryBit = true;
				}
				else
					carryBit = false;
				result.setItem(i, (u_int32)buffer);
			}
			if (carryBit)
				result.setItem(length, 1);

			return result;
		}

		Cvln operator*(const Cvln &left, const Cvln &right)
		{
			Cvln result,sum;
			u_int64 buffer;
			for (u_int64 i = 0; i < left.itemCount; ++i)
			{
				for (u_int64 j = 0; j < right.itemCount; ++j)
				{
					buffer = (u_int64)left.itemPool[i] * (u_int64)right.itemPool[j];
					if ( buffer == 0 )
						continue;
					sum = buffer;
					sum.shiftLeft(i+j);
					result = result + sum;
				}
			}
			return result;
		}


	}
}